using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.ComponentModel;


namespace Qreed.Xna
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class GridComponent : DrawableGameComponent
    {
        private bool _isDirty = true;
        private VertexPositionColor[] _vertices;

        private Color _color = Color.Black;
        /// <summary>
        /// Gets or sets the color.
        /// </summary>
        /// <value>The color.</value>
        public Color Color
        {
            get { return this._color; }
            set 
            {
                _isDirty = true;
                this._color = value; 
            }
        }

        private Vector3 _position;
        /// <summary>
        /// Gets or sets the position.
        /// </summary>
        /// <value>The position.</value>
        public Vector3 Position
        {
            get { return this._position; }
            set { this._position = value; }
        }

        private float _gridSize = 200.0f;
        /// <summary>
        /// Gets or sets the size of the grid.
        /// </summary>
        /// <value>The size of the grid.</value>
        public float GridSize
        {
            get { return this._gridSize; }
            set 
            { 
                this._gridSize = value;
                _isDirty = true;
            }
        }

        private float _gridLine = 5.0f;
        /// <summary>
        /// Gets or sets the grid line.
        /// </summary>
        /// <value>The grid line.</value>
        public float GridLine
        {
            get { return this._gridLine; }
            set 
            { 
                this._gridLine = value;
                _isDirty = true;
            }
        }
			
        private BasicEffect _effect;
        /// <summary>
        /// Gets the effect.
        /// </summary>
        /// <value>The effect.</value>
        public Effect Effect
        {
            get { return this._effect; }
        }

        private ICamera _camera;
        /// <summary>
        /// Gets or sets the camera.
        /// </summary>
        /// <value>The camera.</value>
        public ICamera Camera
        {
            get { return this._camera; }
            set { this._camera = value; }
        }
			
        /// <summary>
        /// Initializes a new instance of the <see cref="GridComponent"/> class.
        /// </summary>
        /// <param name="game">The Game that the game component should be attached to.</param>
        public GridComponent(Game game, ICamera camera)
            : base(game)
        {
            Camera = camera;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// Called when graphics resources need to be loaded. Override this method to load any component-specific graphics resources.
        /// </summary>
        protected override void LoadContent()
        {
            _effect = new BasicEffect(GraphicsDevice, null);
            _effect.VertexColorEnabled = true;

            base.LoadContent();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            if (_isDirty)
                BuildGrid();

            base.Update(gameTime);
        }

        /// <summary>
        /// Builds the grid.
        /// </summary>
        public void BuildGrid()
        {
            if (!Enabled) return;

            List<VertexPositionColor> vertexList = new List<VertexPositionColor>();

            float halfGridSize = GridSize * 0.5f;

            for (float f = -halfGridSize; f <= halfGridSize; f += GridLine)
            {
                Vector3 start = new Vector3();
                Vector3 end = new Vector3();

                start.X = f;
                start.Z = -halfGridSize;

                end.X = f;
                end.Z = halfGridSize;

                vertexList.Add(new VertexPositionColor(start, Color));
                vertexList.Add(new VertexPositionColor(end, Color));

                start = new Vector3();
                end = new Vector3();

                start.X = -halfGridSize;
                start.Z = f;

                end.X = halfGridSize;
                end.Z = f;

                vertexList.Add(new VertexPositionColor(start, Color));
                vertexList.Add(new VertexPositionColor(end, Color));
            }

            _vertices = vertexList.ToArray();
            
            _isDirty = false;
        }

        /// <summary>
        /// Called when the DrawableGameComponent needs to be drawn. Override this method with component-specific drawing code. Reference page contains links to related conceptual articles.
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to Draw.</param>
        public override void Draw(GameTime gameTime)
        {
            if (Enabled)
            {
                _effect.World = Matrix.CreateWorld(Position, Vector3.Forward, Vector3.Up);
                _effect.View = Camera.View;
                _effect.Projection = Camera.Projection;

                Effect.Begin();
                
                foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
                    GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, _vertices, 0, _vertices.Length / 2);

                    pass.End();
                }

                Effect.End();
            }

            base.Draw(gameTime);
        }
    }
}